﻿// 2025/10/17: 首个版本
// 2025/11/12: 优化tab control显示

using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Linq;
using System.Xml;
using System.IO;
using System.Text;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using ASEva;
using ASEva.UIAvalonia;
using ASEva.Utility;
using Avalonia.Controls;

namespace CommonSource
{
    partial class GlobalValuesEditor : DialogPanel
    {
        public GlobalValuesEditor()
        {
            InitializeComponent();
            SetResizableMode(750, 300, 900, 500);
            language = new LanguageSwitch(Resources, AgencyLocal.GetAppLanguage());
            Title = language["title"];

            var model = new GlobalValuesEditorViewModel(this, language, iconResourceName);
            model.InitializeLists();
            DataContext = model;
        }

        public String IconResourceName
        {
            set
            {
                iconResourceName = value;
                Icon = ResourceLoader.Load(value)?.ToAvaloniaWindowIcon();
            }
        }

        private LanguageSwitch language;
        private String iconResourceName;
    }

    partial class GlobalValuesEditorViewModel(Control view, LanguageSwitch language, String iconResourceName) : ObservableObject
    {
        // Initialize //////////////////////////////////////////////////////////////

        public async void InitializeLists()
        {
            var results = await Task.WhenAll([AgencyAsync.GetGlobalParameterKeys(), AgencyAsync.GetGlobalVariableKeys()]);
            GlobalParameterKeys = results[0]?.OrderBy(key => key).ToArray() ?? [];
            GlobalPathKeys = AgencyLocal.GetGlobalPathKeys()?.OrderBy(key => key).ToArray() ?? [];
            LocalGlobalVariableKeys = AgencyLocal.GetGlobalVariableKeys()?.OrderBy(key => key).ToArray() ?? [];
            RemoteGlobalVariableKeys = results[1]?.OrderBy(key => key).ToArray() ?? [];
        }

        // Global parameters ////////////////////////////////////////////////////////

        [ObservableProperty]
        private String[] globalParameterKeys = [];

        [ObservableProperty]
        [NotifyPropertyChangedFor(nameof(CanEditGlobalParameterAsXml))]
        private String selectedGlobalParameterKey;

        partial void OnSelectedGlobalParameterKeyChanged(String value)
        {
            updateSelectedGlobalParameterValue(value);
        }

        private async void updateSelectedGlobalParameterValue(String key)
        {
            EditAsXmlAvailable = false;
            SelectedGlobalParameterValue = null;

            if (key == null) return;

            var paramValue = await AgencyAsync.GetGlobalParameter(key, null) ?? "";
            if (SelectedGlobalParameterKey == null || SelectedGlobalParameterKey != key) return;

            if (checkIsXml(paramValue, out String niceXmlString))
            {
                paramValue = niceXmlString;
                EditAsXmlAvailable = true;
            }
            SelectedGlobalParameterValue = paramValue;
        }

        [ObservableProperty]
        private String selectedGlobalParameterValue;

        [ObservableProperty]
        private bool editingGlobalParameterValue = false;

        [ObservableProperty]
        [NotifyPropertyChangedFor(nameof(CanEditGlobalParameterAsXml))]
        private bool editAsXmlAvailable = false;

        public bool CanEditGlobalParameterAsXml => SelectedGlobalParameterKey != null && EditAsXmlAvailable;

        [RelayCommand]
        private async Task EditGlobalParameterAsXml()
        {
            var id = SelectedGlobalParameterKey;

            var dialog = new XmlEditor(SelectedGlobalParameterValue, iconResourceName);
            await App.RunDialog(dialog);
            if (dialog.BoolResult && SelectedGlobalParameterKey != null && SelectedGlobalParameterKey == id)
            {
                SelectedGlobalParameterValue = dialog.XmlString;
                await AgencyAsync.SetGlobalParameter(id, dialog.XmlString);
            }
        }

        [RelayCommand]
        private void EditGlobalParameter()
        {
            EditingGlobalParameterValue = true;
        }

        [RelayCommand]
        private async Task SaveGlobalParameter()
        {
            EditingGlobalParameterValue = false;
            await AgencyAsync.SetGlobalParameter(SelectedGlobalParameterKey, SelectedGlobalParameterValue);
        }

        // Global paths //////////////////////////////////////////////////////////////

        [ObservableProperty]
        private String[] globalPathKeys = [];

        [ObservableProperty]
        private String selectedGlobalPathKey;

        [ObservableProperty]
        private ObservableCollection<String> globalPathComponents = new ObservableCollection<String>();

        [ObservableProperty]
        private String selectedGlobalPathComponent;

        partial void OnSelectedGlobalPathKeyChanged(string value)
        {
            GlobalPathComponents.Clear();
            if (value == null) return;

            var val = AgencyLocal.GetGlobalPath(value);
            if (val == null) val = "";
            foreach (var path in val.Split(';', StringSplitOptions.RemoveEmptyEntries))
            {
                GlobalPathComponents.Add(path);
            }
        }

        [RelayCommand]
        private void RemoveGlobalPathComponent()
        {
            if (SelectedGlobalPathKey == null || SelectedGlobalPathComponent == null) return;
            GlobalPathComponents.Remove(SelectedGlobalPathComponent);
            AgencyLocal.SetGlobalPath(SelectedGlobalPathKey, String.Join(';', GlobalPathComponents));
        }

        [RelayCommand]
        private async Task AddGlobalFolderPath()
        {
            if (SelectedGlobalPathKey == null) return;

            var val = AgencyLocal.GetGlobalPath(SelectedGlobalPathKey);
            if (val == null) val = "";

            var selected = await App.ShowSelectFolderDialog(view, null, Directory.Exists(val) ? val : null);
            if (selected != null && !GlobalPathComponents.Contains(selected))
            {
                GlobalPathComponents.Add(selected);
                SelectedGlobalPathComponent = selected;
                AgencyLocal.SetGlobalPath(SelectedGlobalPathKey, String.Join(';', GlobalPathComponents));
            }
        }

        [RelayCommand]
        private async Task AddGlobalFilePath()
        {
            if (SelectedGlobalPathKey == null) return;

            var val = AgencyLocal.GetGlobalPath(SelectedGlobalPathKey);
            if (val == null) val = "";

            var selected = await App.ShowOpenFileDialog(view, null, false, File.Exists(val) ? Path.GetDirectoryName(val) : null);
            if (selected != null && !GlobalPathComponents.Contains(selected[0]))
            {
                GlobalPathComponents.Add(selected[0]);
                SelectedGlobalPathComponent = selected[0];
                AgencyLocal.SetGlobalPath(SelectedGlobalPathKey, String.Join(';', GlobalPathComponents));
            }
        }

        // Global variables (Local) ///////////////////////////////////////////////////////////

        public bool ClientSide => AgencyLocal.ClientSide;

        public String LocalGlobalVarsHeader => ClientSide ? language["global-vars-local"] : language["global-vars"];

        [ObservableProperty]
        private String[] localGlobalVariableKeys = [];

        [ObservableProperty]
        private String selectedLocalGlobalVariableKey;

        partial void OnSelectedLocalGlobalVariableKeyChanged(String value)
        {
            SelectedLocalGlobalVariableValue = null;
            if (value == null) return;

            var varValue = AgencyLocal.GetGlobalVariable(value, null) ?? "";

            if (varValue.Length > 1000000) varValue = varValue.Substring(0, 1000000); // A maximum of one million is displayed / 最多显示一百万左右
            if (checkIsXml(varValue, out String niceXmlString))
            {
                varValue = niceXmlString;
            }
            SelectedLocalGlobalVariableValue = varValue;
        }

        [ObservableProperty]
        private String selectedLocalGlobalVariableValue;

        // Global variables (Remote) ///////////////////////////////////////////////////////////

        [ObservableProperty]
        private String[] remoteGlobalVariableKeys = [];

        [ObservableProperty]
        private String selectedRemoteGlobalVariableKey;

        partial void OnSelectedRemoteGlobalVariableKeyChanged(String value)
        {
            updateSelectedRemoteGlobalVariableValue(value);
        }

        private async void updateSelectedRemoteGlobalVariableValue(String key)
        {
            SelectedRemoteGlobalVariableValue = null;
            if (key == null) return;

            var varValue = await AgencyAsync.GetGlobalVariable(key, null) ?? "";
            if (SelectedRemoteGlobalVariableKey == null || SelectedRemoteGlobalVariableKey != key) return;

            if (varValue.Length > 1000000) varValue = varValue.Substring(0, 1000000); // A maximum of one million is displayed / 最多显示一百万左右
            if (checkIsXml(varValue, out String niceXmlString))
            {
                varValue = niceXmlString;
            }
            SelectedRemoteGlobalVariableValue = varValue;
        }

        [ObservableProperty]
        private String selectedRemoteGlobalVariableValue;

        // Utility //////////////////////////////////////////////////////////////

        private bool checkIsXml(String str, out String niceXmlString)
        {
            niceXmlString = "";
            if (str == null) str = "";
            if (str.StartsWith("<?xml"))
            {
                var xml = new XmlDocument();
                try
                {
                    xml.LoadXml(str);
                    niceXmlString = toNiceString(xml);
                    return true;
                }
                catch (Exception) { }
            }
            return false;
        }

        private String toNiceString(XmlDocument xml)
        {
            if (xml == null) return "";

            var memoryStream = new MemoryStream();

            var settings = new XmlWriterSettings();
            settings.Indent = true;
            settings.IndentChars = "    ";

            var xmlWriter = XmlWriter.Create(memoryStream, settings);
            xml.Save(xmlWriter);
            xmlWriter.Close();

            memoryStream.Position = 3; // BOM
            var data = new byte[memoryStream.Length - 3];
            memoryStream.Read(data, 0, (int)memoryStream.Length - 3);
            memoryStream.Close();

            return Encoding.UTF8.GetString(data);
        }
    }
}